import { director } from "cc";
import { Node, instantiate, Prefab } from "cc";
import { lerror } from "../logger/Logger";
import ResLoader, { BundleCallback, BundlesCallback, } from "../resource/ResLoader";
import { UIWindow } from "../comopnent/UIWindow";
import { UIMask } from "../comopnent/UIMask";

export interface IDialogOption {
    text: string;
    onConfirm?: () => void;
    onCancel?: () => void;
    yesBtnTxt?: string;
    noBtnTxt?: string;
}

export interface ILoadinOption {
    bundle: string | string[];
    onComplete?: BundlesCallback;
    onCompleteOne?: BundleCallback;
    onError?: ErrorCallback;
}

export type HintFunction = (text: string) => void;
export type WaitFunction = (text?: string) => Function;
export type MastFunction = (parent?: Node) => UIMask;
export type LoadingFunction = (bundles: string | string[] | ILoadinOption, onComplete?: BundlesCallback, onCompleteOne?: BundleCallback, onError?: ErrorCallback) => void;
export type DialogFunction = (text: string | IDialogOption, onConfirm?: () => void, onCancel?: () => void, yesBtnTxt?: string, noBtnTxt?: string) => void;
export interface UIWindowOption {
    closeOnClickOutside?: boolean;   // 点击窗口外是否关闭窗口
}

export type InitedPrefabCallback = (node: Node) => void;

const EmptyFunc = function () { }

export default class SUI {
    public static UIRoot: Node = null;  // UI根节点
    public static GUIRoot: Node = null; // GUI根节点
    public static LoadingRoot: Node = null; // 加载根节点
    public static DialogRoot: Node = null;  // 弹框根节点
    public static WaitRoot: Node = null;    // 等待根节点
    public static HintRoot: Node = null;    // 提示根节点

    // 遮罩
    public static Mask: MastFunction = () => null;
    // 显示加载界面进行assetbundle加载
    public static Loading: LoadingFunction = EmptyFunc;
    // 显示对话框
    public static Dialog: DialogFunction = EmptyFunc;
    // 显示等待界面
    public static Wait: WaitFunction = () => EmptyFunc;
    // 显示提示
    public static Hint: HintFunction = EmptyFunc;

    /**
     * 初始化预制(这里默认认为资源包和预制已经被加载，使编码时能完全当做一个同步简单的预制初始化功能，如果希望在资源包没有加载的情况下，加载资源包即预制并初始化，请使用InitPrefabSafe)
     * @param assetPath 资源路径(${bundleName}/${pathInBundle})
     * @param parent 父节点路径
     * @returns 初始化出来的节点
     */
    public static InitPrefab(assetPath: string, parent?: Node): Node;
    public static InitPrefab(prefab: Prefab, parent?: Node): Node;
    public static InitPrefab(assetPath: string | Prefab, parent?: Node): Node {
        let prefab: Prefab;
        if (typeof assetPath == "string") {
            prefab = ResLoader.GetAsset(assetPath, Prefab);
            if (prefab == null) {
                lerror(`prefab: ${assetPath} has not loaded`);
                return;
            }
        } else {
            prefab = assetPath;
        }

        let node = instantiate(prefab);
        if (!parent) {
            parent = director.getScene();
        }

        node.setParent(parent);
        return node;
    }

    /**
     * 初始化预制的安全版本(会在预制没有加载的时候加载资源并初始化预制)
     * @param assetPath 资源路径(${bundleName}/${pathInBundle})
     * @param parent 父节点
     * @param onComplete 初始化完成回调
     * @return void
     */
    public static InitPrefabSafe(assetPath: string, parent?: Node, onComplete?: InitedPrefabCallback): void {
        if (!(parent instanceof Node)) {
            onComplete = parent;
            parent = null;
        }

        ResLoader.LoadAsset(assetPath, Prefab, (err: Error, prefab: Prefab) => {
            let node = this.InitPrefab(prefab, parent as Node);
            onComplete && onComplete(node);
        });
    }

    /**
     * InitPrefabSafe的同步版本，可以使用 let node = await InitPrefabSync(...) 来直接获取到初始化出来的节点
     * @param assetPath 
     * @param parent 
     * @returns 
     */
    public static InitPrefabSync(assetPath: string, parent?: Node): Promise<Node> {
        return new Promise((resolve, reject) => {
            ResLoader.LoadAsset(assetPath, Prefab, (err: Error, prefab: Prefab) => {
                if (err) {
                    return reject(err);
                }

                let node = this.InitPrefab(prefab, parent as Node);
                resolve(node);
            });
        });
    }


    public static InitUIPrefab(assetPath: string, parent?: Node) {
        return this.InitPrefab(assetPath, parent ?? this.GUIRoot);
    }

    public static InitUIPrefabSafe(assetPath: string, parent?: Node) {
        this.InitPrefabSafe(assetPath, parent ?? this.GUIRoot);
    }

    public static InitUIPrefabSync(assetPath: string, parent?: Node): Promise<Node> {
        return this.InitPrefabSync(assetPath, parent ?? this.GUIRoot);
    }

    public static InitWindow(assetPath: string, parent?: Node, options?: UIWindowOption) {
        if (!(parent instanceof Node)) {
            options = parent;
            parent = this.GUIRoot;
        }

        let mask: UIMask = this.Mask(parent);
        let windowNode = this.InitPrefab(assetPath, parent as Node);
        let windowComponent = windowNode.getComponent(UIWindow) ?? windowNode.addComponent(UIWindow);
        windowComponent.mask = mask;
        windowComponent.options = options;
        return windowNode;
    }

    public static InitWindowSafe(assetPath: string, onComplete?: InitedPrefabCallback): void;
    public static InitWindowSafe(assetPath: string, parent?: Node, onComplete?: InitedPrefabCallback): void;
    public static InitWindowSafe(assetPath: string, options?: UIWindowOption, onComplete?: InitedPrefabCallback): void;
    public static InitWindowSafe(assetPath: string, parent?: Node, options?: UIWindowOption, onComplete?: InitedPrefabCallback): void;
    public static InitWindowSafe(assetPath: string,
        parent?: Node | UIWindowOption | InitedPrefabCallback,
        options?: UIWindowOption | InitedPrefabCallback,
        onComplete?: InitedPrefabCallback): void {

        if (!onComplete) {
            if (typeof parent === 'function') {
                onComplete = parent;
            } else {
                onComplete = options as InitedPrefabCallback;
                if (!(parent instanceof Node)) {
                    options = parent;
                    parent = this.GUIRoot;
                }
            }
        }

        parent = parent as Node;

        let mask: UIMask = this.Mask(parent);
        this.InitPrefabSafe(assetPath, parent, (windowNode: Node) => {
            let windowComponent = windowNode.getComponent(UIWindow) ?? windowNode.addComponent(UIWindow);
            windowComponent.mask = mask;
            windowComponent.options = options as UIWindowOption;
            onComplete && onComplete(windowNode);
        });
    }

    public static InitWindowSync(assetPath: string, parent?: Node, options?: UIWindowOption): Promise<Node> {
        if (!(parent instanceof Node)) {
            options = parent;
            parent = this.GUIRoot;
        }

        let mask: UIMask = this.Mask(parent);
        return new Promise((resolve, reject) => {
            ResLoader.LoadAsset(assetPath, Prefab, (err: Error, prefab: Prefab) => {
                if (err) {
                    return reject(err);
                }

                let windowNode = this.InitPrefab(prefab, parent as Node);
                let windowComponent = windowNode.getComponent(UIWindow) ?? windowNode.addComponent(UIWindow);
                windowComponent.mask = mask;
                windowComponent.options = options as UIWindowOption;
                resolve(windowNode);
            });
        });
    }
}

export function start_loading(bundles: string | string[] | ILoadinOption, onComplete?: BundlesCallback, onCompleteOne?: BundleCallback, onError?: ErrorCallback) {
    return SUI.Loading(bundles, onComplete, onCompleteOne, onError);
}

export function show_dialog(text: string | IDialogOption, onConfirm?: (() => void), onCancel?: (() => void), yesBtnTxt?: string, noBtnTxt?: string): void {
    return SUI.Dialog(text, onConfirm, onCancel, yesBtnTxt, noBtnTxt);
}

export function show_wait(text?: string): Function {
    return SUI.Wait(text);
}

export function show_hint(text: string): void {
    return SUI.Hint(text);
}